using System;
using UnityEngine;

/// <summary>
/// C#端 对 Camera.WorldToScreenPoint() 的分析
/// </summary>
public class WorldPositionToScreenPosExample2 : MonoBehaviour
{
    public Transform DebugPoint;
    private Vector3 _Position => DebugPoint.position;

    private Camera _Cam => Camera.main;

    private Vector4 _CurrentPositionVS;
    private Vector2 _GameWindowScreenSize;

    private void Update()
    {
        var matrixV = _Cam.worldToCameraMatrix;
        var positionWS = new Vector4(_Position.x, _Position.y, _Position.z, 1);
        _CurrentPositionVS = matrixV * positionWS;
        _GameWindowScreenSize = new Vector2(Screen.width, Screen.height);
    }

    private void OnGUI()
    {
        GUILayout.Label($"当前 PositionVS : {_CurrentPositionVS.ToString("F3")}");
        GUILayout.Label($"内置API Camera.WorldToScreenPoint() : {_Cam.WorldToScreenPoint(_Position).ToString("F3")}");
        GUILayout.Label("可以发现 内置API 的 ScreenPoint.z 为 -PositionVS.z");
        GUILayout.Label("即这个 z 值是 物体到相机的 Z向距离");
        GUILayout.Label($"自行实现 WorldPositionToScreenPos : {WorldPositionToScreenPos(_Position, _Cam).ToString("F3")}");
        GUILayout.Label("Gizmos 中实现了 Z向距离 的展现");
        GUILayout.Label("即这个 z 值是 物体到相机的 Z向距离");
        GUILayout.Label("注意打开 Scene 视窗的 Gizmos");
        GUILayout.Label("Game 视窗打开 Gizmos 也不会看见任何线条");
    }

    private void OnDrawGizmos()
    {
        var camPosition = _Cam.transform.position;

        // 远剪裁面 线框
        Gizmos.color = Color.red;
        var ws1 = _Cam.ScreenToWorldPoint(new Vector3(0, 0, _Cam.farClipPlane));
        var ws2 = _Cam.ScreenToWorldPoint(new Vector3(_GameWindowScreenSize.x, 0, _Cam.farClipPlane));
        var ws3 = _Cam.ScreenToWorldPoint(new Vector3(_GameWindowScreenSize.x, _GameWindowScreenSize.y, _Cam.farClipPlane));
        var ws4 = _Cam.ScreenToWorldPoint(new Vector3(0, _GameWindowScreenSize.y, _Cam.farClipPlane));
        Gizmos.DrawLine(camPosition, ws1);
        Gizmos.DrawLine(camPosition, ws2);
        Gizmos.DrawLine(camPosition, ws3);
        Gizmos.DrawLine(camPosition, ws4);
        Gizmos.DrawLine(ws1, ws2);
        Gizmos.DrawLine(ws2, ws3);
        Gizmos.DrawLine(ws3, ws4);
        Gizmos.DrawLine(ws4, ws1);

        // DebugPoint 剪裁面 线框
        Gizmos.color = Color.blue;
        ws1 = _Cam.ScreenToWorldPoint(new Vector3(0, 0, -_CurrentPositionVS.z));
        ws2 = _Cam.ScreenToWorldPoint(new Vector3(_GameWindowScreenSize.x, 0, -_CurrentPositionVS.z));
        ws3 = _Cam.ScreenToWorldPoint(new Vector3(_GameWindowScreenSize.x, _GameWindowScreenSize.y, -_CurrentPositionVS.z));
        ws4 = _Cam.ScreenToWorldPoint(new Vector3(0, _GameWindowScreenSize.y, -_CurrentPositionVS.z));
        Gizmos.DrawLine(camPosition, ws1);
        Gizmos.DrawLine(camPosition, ws2);
        Gizmos.DrawLine(camPosition, ws3);
        Gizmos.DrawLine(camPosition, ws4);
        Gizmos.DrawLine(ws1, ws2);
        Gizmos.DrawLine(ws2, ws3);
        Gizmos.DrawLine(ws3, ws4);
        Gizmos.DrawLine(ws4, ws1);

        // 近剪裁面 线框
        Gizmos.color = Color.green;
        ws1 = _Cam.ScreenToWorldPoint(new Vector3(0, 0, _Cam.nearClipPlane));
        ws2 = _Cam.ScreenToWorldPoint(new Vector3(_GameWindowScreenSize.x, 0, _Cam.nearClipPlane));
        ws3 = _Cam.ScreenToWorldPoint(new Vector3(_GameWindowScreenSize.x, _GameWindowScreenSize.y, _Cam.nearClipPlane));
        ws4 = _Cam.ScreenToWorldPoint(new Vector3(0, _GameWindowScreenSize.y, _Cam.nearClipPlane));
        Gizmos.DrawLine(camPosition, ws1);
        Gizmos.DrawLine(camPosition, ws2);
        Gizmos.DrawLine(camPosition, ws3);
        Gizmos.DrawLine(camPosition, ws4);
        Gizmos.DrawLine(ws1, ws2);
        Gizmos.DrawLine(ws2, ws3);
        Gizmos.DrawLine(ws3, ws4);
        Gizmos.DrawLine(ws4, ws1);
    }

    /// <summary>
    /// 直接计算 世界坐标 position 的 ScreenPosition
    /// 结果与 cam.WorldToScreenPoint() 一致 
    /// </summary>
    /// <param name="pos"> 世界坐标 </param>
    /// <param name="cam"> 摄像机 </param>
    /// <returns></returns>
    private Vector3 WorldPositionToScreenPos(Vector3 pos, Camera cam)
    {
        var matrixV = cam.worldToCameraMatrix;
        var matrixP = GL.GetGPUProjectionMatrix(cam.projectionMatrix, true);

        var positionWS = new Vector4(pos.x, pos.y, pos.z, 1);
        var positionVS = matrixV * positionWS;
        var positionCS = matrixP * positionVS;
        var positionNDC = new Vector3(positionCS.x, -positionCS.y, positionCS.z) / positionCS.w;
        var positionScreenPos = new Vector3(
            (positionNDC.x / 2f + 0.5f) * Screen.width,
            (positionNDC.y / 2f + 0.5f) * Screen.height,
            -positionVS.z);
        return positionScreenPos;
    }
}